package runner;

import gui.ExperimentsContainer;

import java.util.Hashtable;
import java.util.List;

import log.Logger;
import core.algorithms.SearchParameters;
import core.course.TimeTable;
import core.experiment.Experiment;
import core.experiment.ExperimentResults;
import core.interfaces.IExperimentListener;
import core.interfaces.IProgressListener;
import core.interfaces.IState;

public class ExperimentsRunner<T> implements IProgressListener
{
	private List<Experiment> m_SearchAlgorithmList = null;
	private Hashtable<Experiment, Double> m_AlgorithmToResultMapping = new Hashtable<Experiment, Double>();
	private Hashtable<Experiment, IState<TimeTable>> m_AlgorithmToFinalState = new Hashtable<Experiment, IState<TimeTable>>();
	private IProgressListener m_IProgressListener;
	private IExperimentListener m_IExperimentListener;
	private int m_Finished = 0;
	private double m_TotalExpAsDouble;
	
	public ExperimentsRunner(
			List<Experiment> searchAlgorithmList,
			IProgressListener listner,
			IExperimentListener expListner)
	{
		m_SearchAlgorithmList = searchAlgorithmList;
		m_IProgressListener = listner;
		m_IExperimentListener = expListner;
	}
	
	public void runExperiments()
	{
//		calcMaximumHeuristicCalls();
		m_TotalExpAsDouble = m_SearchAlgorithmList.size();
		Logger logger = new Logger();
		int lineId = 1;
		int lastExperimentId = -1;
		boolean loggingStarted = false;
		for (Experiment experiment : m_SearchAlgorithmList)
		{
			int currentExpId = experiment.getExperimentId();
			
			// init logging
			if (currentExpId != lastExperimentId)
			{
				if (loggingStarted)
				{
					logger.markEnd();
				}
				loggingStarted = true;
				logger.markStart(ExperimentsContainer.COLUMN_NAMES);
			}
			lastExperimentId = currentExpId;
			experiment.addProgressListener(this);
			experiment.start();
			m_IExperimentListener.experimentComplete();
			
			// log line
			logExperiment(experiment, logger, lineId++);
//			m_AlgorithmToResultMapping.put(searchAlgorithm, searchAlgorithm.getFinalResult());
//			m_AlgorithmToFinalState.put(searchAlgorithm, searchAlgorithm.getFinalState());
			
			m_Finished ++;
		}
	}

	private void logExperiment(Experiment experiment, Logger logger, int lineId)
	{
		int n = ExperimentsContainer.COLUMN_NAMES.length;
		Object[] line = new Object[n];
		for (int i = 0; i < n; i++)
		{
			line[i] = getValueAt(experiment, i, lineId);
		}
		logger.writeLine(line);
		
	}

	@Override
	public void percentageComplete(double percentage, int type)
	{
		double value = (100.0 * m_Finished + percentage) / m_TotalExpAsDouble;
		m_IProgressListener.percentageComplete(value, 1);
		
	}

	public Hashtable<Experiment, Double> getAlgorithmToResultMapping()
	{
		return m_AlgorithmToResultMapping;
	}

	public Hashtable<Experiment, IState<TimeTable>> getAlgorithmToFinalState()
	{
		return m_AlgorithmToFinalState;
	}

	
//	private void calcMaximumHeuristicCalls()
//	{
//		m_TotalMaxHeuristicCalls = 0;
//		for (ISearchAlgorithm<T> searchAlgorithm : m_SearchAlgorithmList)
//		{
//			m_TotalMaxHeuristicCalls += searchAlgorithm.getSearchParameters().getMaximumHeuristicCalls();
//		}
//	}

	public Object getValueAt(Experiment experiment, int columnIndex, int lineNumber)
	{
			boolean experimentFinished = experiment.isFinished();
			SearchParameters<TimeTable> searchParameters = experiment.getSearchParameters();
			ExperimentResults searchResults = (experimentFinished ? experiment.getSearchResults() : new ExperimentResults());
			switch(columnIndex)
			{
				// experiment parameters
				case 0: return lineNumber;
				case 1: return experiment.getAlgorithmName();
				case 2: return searchParameters.getHeuristic().toString();
				case 3: return searchParameters.getInitialStateAsString();
				case 4: return searchParameters.getRandomSteps();
				case 5: return experiment.getSpaceSeedRange();
				case 6: return searchParameters.getCourseNumber();
				case 7: return searchParameters.getDrawLimit();
				case 8: return searchParameters.isBoundHeuristicCalls();
				
				// results
				case 9: return getStringValue(searchResults.getAverageCourseNumber(), experimentFinished); 
				case 10: return getStringValue(searchResults.getAverageFinalValue(), experimentFinished);
				case 11: return getStringValue(searchResults.getMaximumFinalValue(), experimentFinished);
				case 12: return getStringValue(searchResults.getMinimumFinalValue(), experimentFinished);
				case 13: return getStringValue(searchResults.getPercentageFinishedAllSteps(), experimentFinished);
				case 14: return getStringValue(searchResults.getAverageTotalStepsTaken(), experimentFinished);
			}
		
		return null;
	}
	
	private String getStringValue(Double value, boolean dontReturnEmptyString)
	{
		if (dontReturnEmptyString)
		{
			return value.toString();
		}
		else
		{
			return "";
		}
	}

	
}
